
//var声明的变量返回了正确的值。这表明，let声明的变量只在它所在的代码块有效。
// {
// 	let a = 10;
// 	var b = 1;
// }

//console.log(a)
//console.log(b)

//for循环的计数器，就很合适使用let命令。 计数器i只在for循环体内有效，在循环体外引用就会报错。
//  for (let i = 0; i < 10; i++) {
//   // ...
// }

// console.log(i);

/*
变量i是var命令声明的，在全局范围内都有效，所以全局只有一个变量i。
每一次循环，变量i的值都会发生改变，而循环内被赋给数组a的函数内部的console.log(i)，里面的i指向的就是全局的i。
也就是说，所有数组a的成员里面的i，指向的都是同一个i，导致运行时输出的是最后一轮的i的值，也就是 10。
*/

// var a = [];
// for (var i = 0; i < 10; i++) {
//   a[i] = function () {
//     console.log(i);
//   };
// }
// a[6](); // 10


/*
使用let，声明的变量仅在块级作用域内有效，最后输出的是 6。
变量i是let声明的，当前的i只在本轮循环有效，所以每一次循环的i其实都是一个新的变量，所以最后输出的是6。
你可能会问，如果每一轮循环的变量i都是重新声明的，那它怎么知道上一轮循环的值，从而计算出本轮循环的值？
这是因为 JavaScript 引擎内部会记住上一轮循环的值，初始化本轮的变量i时，就在上一轮循环的基础上进行计算。
*/

// var a = [];
// for (let i = 0; i < 10; i++) {
//   a[i] = function () {
//     console.log(i);
//   };
// }
// a[6](); // 6


/*
for循环还有一个特别之处，就是设置循环变量的那部分是一个父作用域，而循环体内部是一个单独的子作用域。
上面代码正确运行，输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域，有各自单独的作用域。
*/
// for (let i = 0; i < 3; i++) {
//   let i = 'abc';
//   console.log(i);
// }

/*
不允许重复声明
let不允许在相同作用域内，重复声明同一个变量。
*/

// 报错
// function func() {
//   let a = 10;
//   var a = 1;
// }

// 报错
// function func() {
//   let a = 10;
//   let a = 1;
// }

/*
不能在函数内部重新声明参数。
*/

// function func(arg) {
//   let arg;
// }
// func() // 报错

// function func(arg) {
//   {
//     let arg;
//   }
// }
// func() // 不报错

/*
为什么需要块级作用域？
ES5 只有全局作用域和函数作用域，没有块级作用域，这带来很多不合理的场景。
第一种场景，内层变量可能会覆盖外层变量。

if代码块的外部使用外层的tmp变量，内部使用内层的tmp变量。但是，函数f执行后，输出结果为undefined，原因在于变量提升，导致内层的tmp变量覆盖了外层的tmp变量
*/
// var tmp = new Date();

// function f() {
//   console.log(tmp);
//   if (true) {
//     var tmp = 'hello world';
//   }
// }

// f();//undefined

/*
第二种场景，用来计数的循环变量泄露为全局变量。

变量i只用来控制循环，但是循环结束后，它并没有消失，泄露成了全局变量。
*/

// var s = 'hello';

// for (var i = 0; i < s.length; i++) {
//   console.log(s[i]);
// }

// console.log(i); // 5


/*
ES6 的块级作用域

let实际上为 JavaScript 新增了块级作用域。
下面的函数有两个代码块，都声明了变量n，运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量n，最后输出的值才是 10。
*/

// function f1() {
//   let n = 5;
//   if (true) {
//     let n = 10;
//   }
//   console.log(n); // 5
// }

// f1();

/*
ES6 允许块级作用域的任意嵌套。
下面代码使用了一个五层的块级作用域，每一层都是一个单独的作用域。第四层作用域无法读取第五层作用域的内部变量。
*/
// {{{{
//   {let insane = 'Hello World'}
//   console.log(insane); // 报错
// }}}};


/*
内层作用域可以定义外层作用域的同名变量。
*/

// {{{{
//   let insane = 'Hello World';
//   {let insane = 'Hello World'}
// }}}};

 /*
 块级作用域与函数声明
函数能不能在块级作用域之中声明？这是一个相当令人混淆的问题。

ES5 规定，函数只能在顶层作用域和函数作用域之中声明，不能在块级作用域声明。

下面两种函数声明，根据 ES5 的规定都是非法的。
 */
//  // 情况一
// if (true) {
//   function f() {}
// }

// // 情况二
// try {
//   function f() {}
// } catch(e) {
//   // ...
// }


/// 块级作用域内部的函数声明语句，建议不要使用
// {
//   let a = 'secret';
//   function f() { 
//     return a;
//   }
  
// } 

// 块级作用域内部，优先使用函数表达式
// {
//   let a = 'secret';
//   let f = function () {
//     return a;
//   };
// }

/*
有一个需要注意的地方。ES6 的块级作用域必须有大括号，如果没有大括号，JavaScript 引擎就认为不存在块级作用域。
*/
// // 第一种写法，报错
// if (true) let x = 1;

// // 第二种写法，不报错
// if (true) {
//   let x = 1;
// }